/**
  ******************************************************************************
  * @file    app_X-CUBE-DPower.c
  * @author  STMicroelectronics
  * @version 1.0.0
  * @date    06-Jun-2022
  * @brief   Application program body
  * @details This application example is a Buck PSU converter
  ******************************************************************************
  *
  * Copyright (c) 2019 Biricha Digital Power Limited
  * Copyright (c) 2021(-2022) STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file in
  * the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  ******************************************************************************
  */
/** @addtogroup STM32_DPOW
  * @brief  Digital Power application implemented with a STM32 MCU
  * @{
  */

/** @defgroup STM32_DPOW_PSU
  * @brief  Power Supply Unit feature as an example
  * @{
  */

/** @defgroup PSU_BUCK_CCM
  * @brief  PSU using Phase-Shift Full-Bridge topology in CCM
  *
  *  This example implements a PSU Phase-Shift Full-Bridge topology, in CCM.
  *
  * @{
  */

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "app_X-CUBE-DPower.h"
#include "DPC_Lib_Conf.h"
#include "DCDC_Globals.h"
#include "DCDC_Fault_Processing.h"
#include "DCDC_Control_Layer.h"
#include "DCDC_PWMnCurrVoltFdbk.h"
#include "DPC_STM32_Interface.h"
#ifdef PLOT_WAVEFORM
#include "DPC_DebugMode.h"
#endif

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
LED_Status_t  LED_GREEN_Status = {LED_RIGHT_GREEN_GPIO_Port, LED_RIGHT_GREEN_Pin};   /* Green LED status */
LED_Status_t  LED_RED_Status = {LED_UP_RED_GPIO_Port, LED_UP_RED_Pin};               /* Red LED status */
LED_Status_t  LED_BLUE_Status = {LED_DOWN_BLUE_GPIO_Port, LED_DOWN_BLUE_Pin};        /* Blue LED status */
LED_Status_t  LED_ORANGE_Status = {LED_LEFT_ORANGE_GPIO_Port, LED_LEFT_ORANGE_Pin};  /* Orange LED status */
uint16_t      LED_LastFaultShown = (uint16_t)DCDC_NO_ERROR;    /* last DCDC fault shown with LED blinking */
// PBo - To Be Improved
/* Actual usage of the DCDC_DataFloat{} refer to only a type III controller */
/* This might not be as crystal clear for users that a type II is a type III controller with biggest coefficients reset */
#ifdef DEBUG_COMP_OUT
// Slope compensation debug mode capability
float    Vdrop = 0.25;      // Wanted slope for the current compensation
uint16_t NumUpdates = 100;  // Max steps update per slope period
#endif

/* Private function prototypes -----------------------------------------------*/
/**
  * @brief  This function is executed in case of initialization error occurrence.
  * @param  None
  * @retval None
  */
static void Init_Error_Handler(void)
{
  /* Set red LED fast blinking when error */
  LED_SetParams(&LED_RED_Status, LED_BLINK_INF, LED_STARTUP_BLINK_NUM, LED_BLINK_PERIOD_MS, 0);
  while (1)
  {
    /* Manage red LED */
    LED_Task(&LED_RED_Status);
  }
}

/**
  * @brief  This function Initialize control variables
  * @param  None
  * @retval None
  *
  */
static void InitControlParameters(void)
{
  /* Set actual and init ramp-up reference at last Vout measure */
  hVout_Reference = DCDC_MeasureStruct.hVout;
  hVout_Reference_init = DCDC_MeasureStruct.hVout;

  /* Init Reference value */
  vLoop.m_Ref      = (float)0;  // reset vLoop reference
}

#ifdef DEBUG_COMP_OUT
/**
  * @brief This set the slope compensation for the DAC
  * @param  uSlope: Slope compensation in V/ms
  * @param  uSlopeTicks: Number of Ticks to update slope compensation points
  * @retval None
  */
static void SetRamp( float uSlope, uint16_t uSlopeTicks )
{
  /* Update the sawtooth waveform generator with the required step size */
  LL_DAC_SetWaveSawtoothStepData(((&DPC_APPL_DACx)->Instance), DACx_CHANNEL2, (uint16_t)(((uSlope/uSlopeTicks) * DAC_MAX * 16)/VREF));
  __HAL_HRTIM_SETCOMPARE( &DPC_APPL_HRTIM, HRTIM_TIMERINDEX_TIMER_C, HRTIM_COMPAREUNIT_2, (uint16_t)(Demo.periodBuck / uSlopeTicks));
}
#endif

/**
  * @brief  This function handles the loads.
  * @param  None
  * @retval None
  */
void LoadHandler(void)
{
  switch (Demo.NbrActiveLoad)
  {
  case 2:
    /* Enable Load_1 */
    HAL_GPIO_WritePin(GPIOC, BUCKBOOST_LOAD_1_Pin, GPIO_PIN_SET);
    /* Enable Load_2 */
    HAL_GPIO_WritePin(GPIOC, BUCKBOOST_LOAD_2_Pin, GPIO_PIN_SET);
    break;
  case 1:
    /* Enable Load_1 */
    HAL_GPIO_WritePin(GPIOC, BUCKBOOST_LOAD_1_Pin, GPIO_PIN_SET);
    /* Disable Load_2 */
    HAL_GPIO_WritePin(GPIOC, BUCKBOOST_LOAD_2_Pin, GPIO_PIN_RESET);
    break;
  case 0:
  default:
    /* Disable Load_1 */
    HAL_GPIO_WritePin(GPIOC, BUCKBOOST_LOAD_1_Pin, GPIO_PIN_RESET);
    /* Disable Load_2 */
    HAL_GPIO_WritePin(GPIOC, BUCKBOOST_LOAD_2_Pin, GPIO_PIN_RESET);
    break;
  }
}

/* External variables --------------------------------------------------------*/
#ifdef PLOT_WAVEFORM
extern WAVEFORM_DISPLAY_INFO_t Waveform;
#endif
G4_Demo_t      Demo;
DCDC_DataFloat myDcdc;
CNTRL_3p3zDataFloat vLoop;                          /**< The voltage loop controller (if used) */
CNTRL_3p3zDataFloat iLoop;                          /**< The current loop controller (if used) */

uint16_t       bPowerUp;

/* External functions ---------------------------------------------------------*/
/**
  * @brief Topology initialisation
  * @details
  * This process is initialising the selected topology.
  */
void MX_X_CUBE_DPower_Init(void)
{
  /* Set up DCDC controller structure */
  DCDC_config( &myDcdc, DCDC_BUCK, DCDC_PWM, DCDC_VOLTAGE );

  /* Initialize Global variables */
  Demo.periodBuck = 0;
  Demo.NbrActiveLoad = 0;
  bPowerUp = 0;

  /* Init 3p3z using the CPU */
  CNTRL_3p3zInit(&vLoop, REF,
                 A1, A2, A3,
                 B0, B1, B2, B3,
                 K, (float_t)DUTY_TICKS_MIN, (float_t)DUTY_TICKS_MAX );
  /* Initialise soft start ramp */
  CNTRL_RampFloatConfig( &myDcdc.vRamp, 0, NOMINAL_OUT_VOLTAGE, STARTUP_DCDC_RAMP_DURATION, 1000 );

  /* Perform an ADCx automatic self-calibration and enable ADC */
  if (HAL_ADCEx_Calibration_Start(&DPC_APPL_ADCx, ADC_SINGLE_ENDED) != HAL_OK)
  {
    /* ADC initialization Error */
    Init_Error_Handler();
  }

  #ifndef RUN_OPEN_LOOP
  /* Start ADCx using interrupts */
  if (HAL_ADC_Start_IT(&DPC_APPL_ADCx) != HAL_OK)
  {
    /* ADC initialization Error */
    Init_Error_Handler();
  }

  /* Start ADCx EOC interrupts */
  __HAL_ADC_ENABLE_IT(&DPC_APPL_ADCx, (ADC_IT_EOC));
  #endif

  /* Calculate PWM period and update timer register */
  Demo.periodBuck = HAL_HRTIM_FreqToTicks(&DPC_APPL_HRTIM, HRTIM_TIMERINDEX_TIMER_C, SWITCHING_FREQ_HZ);
  __HAL_HRTIM_SETPERIOD(&DPC_APPL_HRTIM, HRTIM_TIMERINDEX_TIMER_C, Demo.periodBuck);

  #ifdef RUN_OPEN_LOOP
  /* Open loop so run with a small fixed duty cycle */
  __HAL_HRTIM_SETCOMPARE(&DPC_APPL_HRTIM, HRTIM_TIMERINDEX_TIMER_C, HRTIM_COMPAREUNIT_1, Demo.periodBuck * 5 / 100);
  #else
  /* Set max duty to 80% of period */
  __HAL_HRTIM_SETCOMPARE(&DPC_APPL_HRTIM, HRTIM_TIMERINDEX_TIMER_C, HRTIM_COMPAREUNIT_1, Demo.periodBuck * 80 / 100);
  #endif

  #ifdef PLOT_WAVEFORM
  /* Plot waveform using IAR IDE */
  Waveform.m_State = WAVEFORM_WAIT_FOR_TRIGGER;
  #endif

  /* Set Green LED ON at power on, others to power off */
  LED_SetParams(&LED_GREEN_Status, LED_ON, 0, 0, 0);
  LED_SetParams(&LED_BLUE_Status, LED_OFF, 0, 0, 0);
  LED_SetParams(&LED_ORANGE_Status, LED_OFF, 0, 0, 0);
  LED_SetParams(&LED_RED_Status, LED_OFF, 0, 0, 0);

  myDcdc.m_DCDC_State = DCDC_IDLE;

  /* Cancel previous errors if any */
  myDcdc.m_DCDC_System_Fault &= ~DCDC_OUT_OVER_VOLT;
  myDcdc.m_DCDC_System_Fault &= ~DCDC_OVER_CURRENT;
  myDcdc.m_DCDC_System_Fault &= ~DCDC_OVER_TEMP;
}

/**
  * @brief Topology recurrent process
  * @details
  * This process is operating the selected topology.
  */
void MX_X_CUBE_DPower_Process(void)
{
    /* -------------------- */
    /* Manage all LEDs used */
    /* -------------------- */
    LED_Task(&LED_GREEN_Status);
    LED_Task(&LED_RED_Status);
    LED_Task(&LED_BLUE_Status);
    LED_Task(&LED_ORANGE_Status);

    /* ---------------- */
    /* Fault management */
    /* ---------------- */

    /* Check is a fault has occurred on DC/DC converter */
    DCDC_FaultCheck();

    /* Toggle a pin to measure how long it takes to go around state machine */
//    HAL_GPIO_TogglePin(DEBUG_PORT, DEBUG_PIN);

    /* --------------------- */
    /* General State Machine */
    /* --------------------- */
    switch( myDcdc.m_DCDC_State )
	{
      case DCDC_IDLE:    /** @arg  \a DCDC_IDLE state: wait for input voltage and frequency conditions */
        /* Set Green LED slow blinking */
        LED_SetParams(&LED_GREEN_Status, LED_BLINK_N, LED_STARTUP_BLINK_NUM, LED_BLINK_PERIOD_LONG_MS, LED_BLINK_REPETITION_PERIOD_MS);
        /* Set Red LED OFF */
        LED_SetParams(&LED_RED_Status, LED_OFF, 0, 0, 0);

        /* Check about potential DC/DC error(s) */
        if (myDcdc.m_DCDC_System_Fault == DCDC_NO_ERROR)
        {
          #ifndef DEBUG_MODE
          /* Set next state */
          myDcdc.m_DCDC_State = DCDC_INIT;
          #else
          /* Block state machine in IDLE state until a debugger command */
          #endif

          /* Set Green LED normal blinking until ramp up is complete or until any error detection */
          LED_SetParams(&LED_GREEN_Status, LED_BLINK_INF, LED_STARTUP_BLINK_NUM, LED_BLINK_PERIOD_MS, LED_BLINK_REPETITION_PERIOD_MS);
          /* Set Red LED OFF */
          LED_SetParams(&LED_RED_Status, LED_OFF, 0, 0, 0);
        }
        else
        {
          #ifndef DEBUG_MODE
          /* Set next state */
          myDcdc.m_DCDC_State = DCDC_ERROR;
          #else
          /* Block state machine in IDLE state until a debugger command */
          #endif

          /* Set Green LED slow blinking */
          LED_SetParams(&LED_GREEN_Status, LED_BLINK_N, LED_STARTUP_BLINK_NUM, LED_BLINK_PERIOD_LONG_MS, LED_BLINK_REPETITION_PERIOD_MS);
          /* Set Red LED slow blinking */
          LED_SetParams(&LED_RED_Status, LED_BLINK_N, LED_STARTUP_BLINK_NUM, LED_BLINK_PERIOD_LONG_MS, LED_BLINK_REPETITION_PERIOD_MS);
        }
        break;

        case DCDC_INIT:    /** @arg  \a DCDC_INIT state: reinitialize control variables and enable output */
        /* Reinit control variables */
        InitControlParameters();

        /* Activate Power devices */
        if (bDCDC_OutputEnabled == false)
        {
          /* Enable PWMs */
          DCDC_PWMOutputEnable(&DPC_APPL_HRTIM);
          /* Enable Gate Driver (=T6 for Buck only mode usage) */
          DCDC_GateDriverEnable();
          /* Start HRTIM counter and interrupts */
          HAL_HRTIM_WaveformCounterStart(&DPC_APPL_HRTIM, HRTIM_TIMERID_TIMER_C);
        }

        /* Set delay timer for Vbus ramp-up */
        SetDelayTime(&hDCDC_StartUp_TimeLeft, STARTUP_DCDC_RAMP_DURATION);

        #ifndef DEBUG_MODE
        /* Set next state */
        myDcdc.m_DCDC_State = DCDC_START;
        #else
        /* Block state machine in INIT state until a debugger command */
        #endif
        break;

      case DCDC_START:     /**  @arg \a DCDC_START state: ramp-up Vout for DC/DC regulation */
        /* Enable power up */
        bPowerUp = 1;

        /* If start-up time is elapsed, enable undervoltage check so if bus can't be charged the system goes in DCDC_STOP/DCDC_ERROR state */
        if (DelayTimeIsElapsed(&hDCDC_StartUp_TimeLeft) == true)
        {
          /* Enable undervoltage check */
          bCheckUndervoltageEnabled = true;
        }

        /* Vout ramp-up */
        if (vLoop.m_Ref < STARTUP_FINAL_OUT_VOLTAGE)
        {
            /* Still ramping-up output voltage */
        }
        else
        {
          /* Set ramp-up complete flag in DCDC status variable */
          myDcdc.m_DCDC_System_Fault |= DCDC_START_UP_OK;
          #ifndef DEBUG_MODE
          /* Set next state */
          myDcdc.m_DCDC_State = DCDC_RUN;
          #else
          /* Block state machine in START state until a debugger command */
          #endif

          /* Set Green LED fast blinking forever */
          LED_SetParams(&LED_GREEN_Status, LED_BLINK_INF, LED_RUN_BLINK_NUM, LED_BLINK_PERIOD_SHORT_MS, 0);
          /* Set Red LED OFF */
          LED_SetParams(&LED_RED_Status, LED_OFF, 0, 0, 0);
        }
        break;

        case DCDC_RUN:           /** @arg \a RUN state: normal operation of DC/DC */
        /* If start-up time is elapsed, enable undervoltage check so if bus can't be charged the system goes in DCDC_STOP/DCDC_ERROR state */
        if (DelayTimeIsElapsed(&hDCDC_StartUp_TimeLeft) == true)
        {
          /* Enable undervoltage check */
          bCheckUndervoltageEnabled = true;
          #ifdef DEBUG_COMP_OUT
          SetRamp(Vdrop, NumUpdates);
          #endif
        }
        HAL_Delay(500);

        /* Automatic toggling from 50% to 100% load for transient measurements */
        if (LED_BLUE_Status.bActualOn == true)
        {
          if (Demo.NbrActiveLoad == 1)
          {
            Demo.NbrActiveLoad = 2;
          }
          else
          {
            Demo.NbrActiveLoad = 1;
          }
        LoadHandler();
        }
        break;

        case DCDC_STOP:          /** @arg \a DCDC_STOP state: stop of conversion if any fault has occurred */
        /* Set Green LED OFF */
        LED_SetParams(&LED_GREEN_Status, LED_OFF, 0, 0, 0);
        /* Set Red LED fast blinking forever */
        LED_SetParams(&LED_RED_Status, LED_BLINK_INF, LED_ERROR_BLINK_NUM, LED_BLINK_PERIOD_SHORT_MS, 0);

        /* Power down */
        bPowerUp = 0;

        /* Deactivate Power devices */
        if (bDCDC_OutputEnabled == true)
        {
          /* Disable Gate Driver (=T6 for Buck only mode usage) */
          DCDC_GateDriverDisable();
          /* Disable PWMs */
          DCDC_PWMOutputDisable(&DPC_APPL_HRTIM);
        }

        /* Check if there was an error, then update the last fault variable */
        if ((myDcdc.m_DCDC_System_Fault & ~DCDC_START_UP_OK) != DCDC_NO_ERROR)
        {
          /* Store the last fault variable */
          myDcdc.m_DCDC_Last_System_Fault = myDcdc.m_DCDC_System_Fault;
        }

        /* Reset update control params */
        bUpdateControlParamEnabled = false;      /* not used */

        /* Disable undervoltage check */
        bCheckUndervoltageEnabled = false;

        /* Cancel undervoltage protection fault - comment if the short circuit fault is not recoverable */
        myDcdc.m_DCDC_System_Fault &= ~DCDC_SHORT_CIRCUIT;

        /* Cancel ramp-up complete flag in DCDC status */
        myDcdc.m_DCDC_System_Fault &= ~DCDC_START_UP_OK;

        #ifndef DEBUG_MODE
        /* Set next state */
        myDcdc.m_DCDC_State = DCDC_ERROR;
        #else
        /* Block state machine in STOP state until a debugger command */
        #endif
        break;

        case DCDC_ERROR:         /** @arg \a DCDC_ERROR state: error detected state */
        /* Set Green LED slow blinking */
        LED_SetParams(&LED_GREEN_Status, LED_BLINK_N, LED_STARTUP_BLINK_NUM, LED_BLINK_PERIOD_LONG_MS, LED_BLINK_REPETITION_PERIOD_MS);
        /* Set Red LED slow blinking */
        LED_SetParams(&LED_RED_Status, LED_BLINK_N, LED_STARTUP_BLINK_NUM, LED_BLINK_PERIOD_LONG_MS, LED_BLINK_REPETITION_PERIOD_MS);

        /* Error has been recovered */
        if (myDcdc.m_DCDC_System_Fault == DCDC_NO_ERROR)
        {
          #ifndef DEBUG_MODE
          /* Set next state */
          myDcdc.m_DCDC_State = DCDC_IDLE;
          #else
          /* Block state machine in ERROR state until a debugger command */
          #endif
        }
        /* Error is still existing */
        else
        {
          /* Set delay timer for wait status */
          SetDelayTime(&hDCDC_Wait_TimeLeft, WAIT_DCDC_TIME_DURATION);

          #ifndef DEBUG_MODE
          /* Set next state */
          myDcdc.m_DCDC_State = DCDC_WAIT;
          #else
          /* Block state machine in ERROR state until a debugger command */
          #endif
        }
        break;

        case DCDC_WAIT:    /** @arg  \a DCDC_WAIT state:  wait a delay time before return in DCDC_ERROR state */
        /* Wait a few before trying to restart */
        if (DelayTimeIsElapsed(&hDCDC_Wait_TimeLeft) == true)
        {
          #ifndef DEBUG_MODE
          myDcdc.m_DCDC_State = DCDC_ERROR;
          #else
          /* Block state machine in WAIT state until a debugger command */
          #endif
        }
        break;

        case DCDC_FAULT:         /** @arg \a DCDC_FAULT state: persistent state after a fault */
        /* Set Green LED ON */
        LED_SetParams(&LED_GREEN_Status, LED_ON, 0, 0, 0);
        /* Set Red LED ON */
        LED_SetParams(&LED_RED_Status, LED_ON, 0, 0, 0);
        /* Block state machine in FAULT state until a debugger command */
        break;

        default:
        #ifndef DEBUG_MODE
        /* Restart as system reset */
        MX_X_CUBE_DPower_Init();
        #else
        /* Block state machine in default state until a debugger command */
        #endif
        break;
    }
    /*-- end of switch-case State Machine --*/
}

/**
  * @}  end PSU_BUCK_CCM
  */

/**
  * @}  end STM32_DPOW_PSU
  */

/**
  * @}  end STM32_DPOW
  */
